
import java.util.Random;

import Pieces.*;

public class Board {

    public static final int BOARD_WIDTH = 10;
    public static final int BOARD_HEIGHT = 20;
    public static final int PIECE_SIZE = 4;
    private static final int[] probs = {L.PROBABILITY, Pipe.PROBABILITY,
                                        ReverseL.PROBABILITY, ReverseSquigly.PROBABILITY,
                                        Square.PROBABILITY, Squigly.PROBABILITY,
                                        Tee.PROBABILITY};
    //the probabilities of each piece.  If you change the order, change the order in getRandomPiece()

    private Random rand;
     public Piece activePiece;
     public Piece nextPiece;
     public BlockType[][] blob;
     public BlockType[][] board;
     
     public Board(){
    	 board = new BlockType[BOARD_HEIGHT][BOARD_WIDTH];
    	 blob = new BlockType[BOARD_HEIGHT][BOARD_WIDTH];
    	 initBoard();
    	 initBlob();
    	 
    	 activePiece = getRandomPiece(); //create new Piece 4x4
    	 nextPiece = getRandomPiece(); //create new Piece 4x4
         //rand = new Random();
     }
     

     public void initBoard(){
    	 for(int i=0;i<BOARD_HEIGHT;i++){
    		 for(int j=0;j<BOARD_WIDTH;j++){
    			 board[i][j] = BlockType.NONE;
    		 }
    	  }
     }
     
     public void initBlob(){
    	 for(int i=0;i<BOARD_HEIGHT;i++){
    		 for(int j=0;j<BOARD_WIDTH;j++){
    			 blob[i][j] = BlockType.NONE;
    		 }
    	  }
     }

     //used for debug purposes to show what what the blob looks like
     public void printBlob()
     {
         for(int i = 0; i < BOARD_HEIGHT; i++)
         {
             for(int j = 0; j < BOARD_WIDTH; j++)
             {
                 System.out.print(blob[i][j] + "\t");
             }

             System.out.println("");
         }
     }
     
     public Piece getRandomPiece()
     {
    	   rand = new Random();
           int num = rand.nextInt(100);

         int piece = 0;
         while(num > 0) //get which section of probabilities the random number fell
         {
             num -= probs[piece];
             piece++;
         }

         switch(piece - 1)
         {
             case 0: return new L();
             case 1: return new Pipe();
             case 2: return new ReverseL();
             case 3: return new ReverseSquigly();
             case 4: return new Square();
             case 5: return new Squigly();
             default: return new Tee();
         }

//         if(num >75)  
//        	 return new Pipe();
//         else if(num > 50 && num <=75)
//        	 return new Tee();
//         else if(num > 25 && num <=50)
//        	 return new L();
//         else 
//        	 return new Square();
     }
     
     public Piece getNextPiece() {
    	 return nextPiece;
     }
     
     public void rotateActivePiece()
     {
    	 activePiece.changeState();

         if(testRotateCollision())
         {
             activePiece.restoreState();
         }
         else
         {
            translatePiecesToBoard();
         }
     }

     //use negative for left, positive for right
     public void movePiece(int howMuch)
     {
         int x = activePiece.getPosition().x;
         int y = activePiece.getPosition().y;
         activePiece.setPosition(x + howMuch, y);
         if(testLeftRightCollision() || testDropCollision())
         {
             activePiece.setPosition(x, y);
         }
         else
         {
             translatePiecesToBoard();
         }
     }

     //drops the piece down as far as it will go
     public void instantDrop()
     {
         int x = activePiece.getPosition().x;
         while(!testDropCollision()) //while no collision
         {
             activePiece.setPosition(x, activePiece.getPosition().y + 1);
         }

         //since it goes until it collides with something, have to subtract 1
         activePiece.setPosition(x, activePiece.getPosition().y - 1);
         translatePiecesToBoard();
     }
     
     /*
      * moves the active piece down by 1
      */
     public void dropActivePiece()
     {
    	 int col = activePiece.getPosition().x;
    	 int row = activePiece.getPosition().y;
         activePiece.setPosition(col, row+1);

         if(testDropCollision())
         {
             activePiece.setPosition(col, row);
             swapActiveAndNextPiece();
         }
    	 
    	 translatePiecesToBoard();
     }
     
     /**
      * rowIsEmpty - Determines whether a given row is void of blocks.
      * @author Clinton Freeman
      * @version 2010.09.11
      */
	 private boolean rowIsEmpty(int row) {
		 for(int i = 0; i < BOARD_WIDTH; i++) {
			 //If just one block is full, return false
			 if(blob[row][i] != BlockType.NONE)
				 return false;
		 }
    	 
		 //All blocks were empty
		 return true;
	 }
     
	 /**
	  * rowIsFull - Determines whether a given row is filled with blocks.
	  * @author Clinton Freeman
	  * @version 2010.09.11
	  */
     private boolean rowIsFull(int row) {
    	 for(int i = 0; i < BOARD_WIDTH; i++) {
    		 //If just one block is empty, return false
    		 if(blob[row][i] == BlockType.NONE)
    			 return false;
    	 }
    	 
    	 //All blocks were filled
    	 return true;   	 
     }
     
	 /**
	  * clearRow - Called after we've determined that a row is full.
	  * Should move each row that is above it down by one.
	  * @author Clinton Freeman
	  * @version 2010.09.11
	  */
	 private void clearRow(int fullRow) {
		 int firstEmptyRow = 0;

		 //Begin by finding the first empty row above the filled row.
		 for(int i = (fullRow - 1); i > 0; i--)
			 if(rowIsEmpty(i))
				 firstEmptyRow = i;
		 
		 //For each row in between the row that is full and the first
		 //row that is empty, move it down by one.
		 for(int i = (fullRow - 1); i > firstEmptyRow; i--) {
			 for(int j = 0; j < BOARD_WIDTH; j++) {
				 blob[i+1][j] = blob[i][j];
			 } 
		 }
	 }

	 /**
	  * checkForFullRows - Iterates through the blob to determine if a row 
	  * has been filled with blocks, and shifts rows above it down should
	  * it find one.
	  * @author Clinton Freeman
	  * @version 2010.09.11
	  */
	 private void checkForFullRows() {
		 for (int i = 0; i < BOARD_HEIGHT; i++)
			 if(rowIsFull(i)) {
				 clearRow(i);
				 Game.getInstance().score();
			 }
	 }
	 
//	 /**
//	  * nextPieceIsBlocked - Determines whether or not the next piece will
//	  * end the game by performing various checks on the first several rows
//	  * of the blob.
//	  * @TODO: This needs to be improved, because it's not very accurate.
//	  * @author Clinton Freeman
//	  * @version 2010.09.12
//	  */
//	 private boolean nextPieceIsBlocked() {
//		 //3 4 5 6
//		 //Crude method: if any of the indicies listed above
//		 //are full in the first two rows, then end the game.
//		 for(int i = 0; i < 2; i++) {
//			 for(int j = 3; j < 7; j++) {
//				 if(blob[i][j] != BlockType.NONE) {
//					 return true;
//				 }
//			 }
//		 }
//
//		 return false;
//	 }

         private boolean gameEnded()
         {
             return (testDropCollision()) ? true : false;
         }
     
	 /*
	  * makes the next piece become the active piece
	  * also handles checking for, and responding to
	  * rows being filled.
	  */
	 public void swapActiveAndNextPiece() {
		 activePieceToBlob();
		 checkForFullRows();

		 activePiece = nextPiece;
		 nextPiece = getRandomPiece();

                 if(gameEnded())
                 {
			 Game.getInstance().endGame();
		 }
	 }
     
     /*
      * translates the active piece to the blob
      */
     public void activePieceToBlob(){
    	 int cols = (int)activePiece.getPosition().getX();
    	 int rows = (int)activePiece.getPosition().getY();
    	 BlockType[][] piece = activePiece.getCurrentBlockArray();
    	 for(int i=0;i < PIECE_SIZE;i++){
		   for(int j=0;j < PIECE_SIZE;j++){
			   if(piece[i][j] != BlockType.NONE)
			   {//don't want to cover up previous pieces with BlockType.NONE (i.e. white)
				   blob[rows+i][cols+j] = piece[i][j];
			   }
		   }
    	 }
     }


     public void translatePiecesToBoard(){
    	 //First, copy the "blob's" blocks to our board
    	 BlockType[][] piece = activePiece.getCurrentBlockArray();
    	 
    	  for(int i=0;i<BOARD_HEIGHT;i++) {
    		 for(int j=0;j<BOARD_WIDTH;j++) {
    			 board[i][j] = blob[i][j];
    		 }
    	  }
    	 

    	 //Second, copy the "activePiece" blocks to the 
    	 //appropriate place on the board using the coordinates
    	 //of the activePiece
    	 int cols = activePiece.getPosition().x;
    	 int rows = activePiece.getPosition().y;
    	 
    	 for(int i=0;i < PIECE_SIZE;i++){
		   for(int j=0;j < PIECE_SIZE;j++){
               if(piece[i][j] != BlockType.NONE) //if you add all the blocks, then you get an out-of-bounds when not needed and you cover up other pieces with WHITE
               {
                    board[rows+i][cols+j] = piece[i][j];
               }
		   }
    	 }
    	 
     }
     
     public BlockType[][] getBoard(){
    	 return board;
     }
     
   
     private boolean testDropCollision()
     {
         BlockType[][] currentState = activePiece.getCurrentBlockArray();

         int starty = activePiece.getPosition().y;

         if(testBottomCollision())
             return true;

         int stopy = starty + PIECE_SIZE;

         int startx = activePiece.getPosition().x;

         for(int y = 0; y < PIECE_SIZE; y++)
         {
             for(int x = 0; x < PIECE_SIZE; x++)
             {
                 if(currentState[y][x] != BlockType.NONE)
                 {
                     if(blob[starty + y][startx + x] != BlockType.NONE)
                     {
                         return true;
                     }
                 }
             }
         }

         return false;
     }

     //returns true if collision, false if not
     private boolean testBottomCollision()
     {
         BlockType[][] currentState = activePiece.getCurrentBlockArray();
         int starty = activePiece.getPosition().y;

         boolean done = false;
         int bottom;
         for(bottom = 3; bottom >= 0; bottom--)
         {
             for(int x = 0; x < 4; x++)
             {
                 if(currentState[bottom][x] != BlockType.NONE)
                 {
                     done = true;
                     break;
                 }
             }

             if(done)
             {
                 break;
             }
         }

         return (bottom + starty > (BOARD_HEIGHT - 1)) ? true : false;
     }

     //returns true if collision, false if not
     private boolean testLeftRightCollision()
     {
         int piecex = activePiece.getPosition().x;

         BlockType[][] currentState = activePiece.getCurrentBlockArray();
         int left = PIECE_SIZE;
         int right = 0;

         for(int y = 0; y < PIECE_SIZE; y++)
         {
             for(int x = 0; x < PIECE_SIZE; x++)
             {
                 if(currentState[y][x] != BlockType.NONE)
                 {
                     if(x < left)
                     {
                         left = x;
                     }
                     if(x > right)
                     {
                         right = x;
                     }
                 }
             }
         }

         return ((left + piecex) < 0 || (right + piecex) > BOARD_WIDTH - 1) ? true : false;
     }

     //returns true if collision, false if not
     public boolean testRotateCollision()
     {
         return (testLeftRightCollision() || testDropCollision()) ? true : false;
     }
}
